# coding=utf-8
# __author__ = "James Zhang"
# !/usr/bin/python
# -*- coding: utf-8 -*-
from __future__ import print_function, division
# import functools
from sqlalchemy import create_engine
# from sqlalchemy.types import CHAR, Integer, String
# from sqlalchemy.schema import ForeignKeyConstraint, ForeignKey
# from sqlalchemy import ForeignKey
import sqlalchemy.pool
from sqlalchemy.ext.declarative import declarative_base, declared_attr
# from sqlalchemy.ext.declarative.base import _as_declarative
from sqlalchemy.orm import sessionmaker, scoped_session

import copy


from core.conf import settings
import core.utils.exceptions
from core.utils.functional import Singleton


class DB(object):
    """为settings.py中配置的DATABASE选择合适的驱动字符串
    """
    DB_CONNECT_STRING = "{backend}://{user}:{password}@{host}:{port}/{db}?charset={charset}"
    DB_TYPE = {
        'mysql': "mysql+mysqldb",  # 已经在mysql 5.6中验证OK
        'mssql': "mssql+pymssql",  # 暂未验证
        'oracle': "oracel",  # 暂未验证
    }

    def __init__(self):
        self.conn_str = None
        data_base = getattr(settings, 'DATABASE', None)
        if data_base is None:
            raise core.utils.exceptions.SettingError("You must be configure the DATABASE in settings.py")
        assert isinstance(data_base, dict), "DATABASE must be a dict object"
        default = data_base.get('default', '')
        if not default:
            raise core.utils.exceptions.SettingError("You must be configure the DATABASE['default'] in settings.py")
        assert isinstance(data_base, dict), "DATABASE['default'] must be a dict object"
        backend = default.get('backend', 'mysql')
        if backend not in self.DB_TYPE:
            raise core.utils.exceptions.SettingError("Can't found this drive: %s" % backend)
        try:
            config = copy.deepcopy(default)
            config['backend'] = self.DB_TYPE[backend]
            self.conn_str = self.DB_CONNECT_STRING.format(**config)
        except KeyError, e:
            raise core.utils.exceptions.SettingError("Cat't found key: %s in setting.py DATABASE['default']" % e)


class Engine(object):
    # __metaclass__ = Singleton

    def __init__(self):
        self.engine = create_engine(
            DB().conn_str, echo=settings.ECHO,
            convert_unicode=True,
            poolclass=sqlalchemy.pool.NullPool
            # pool_size=20,
            # max_overflow=100
        )

        # self.db = sessionmaker(bind=self.engine)()

        self._session_factory = sessionmaker(bind=self.engine)
        self._Session = scoped_session(self._session_factory)
        self.db = self._Session()

        # scoped_session
        # some_session = self._Session()
        # some_other_session = self._Session()
        # some_session is some_other_session
        # >>> True

# DBSession = sessionmaker(bind=Engine().engine)
# _session = DBSession()

Base = declarative_base()


class Query(object):
    def __get__(self, instance, owner):
        return owner.db.query(owner)


class BaseModel(Base):
    __abstract__ = True

    query = Query()

    @declared_attr
    def db(cls):
        return Engine().db

    @declared_attr
    def __tablename__(cls):
        return cls.__name__.lower()

    def save(self):
        """保存数据
        """
        self.db.add(self)
        self.db.commit()

    def delete(self):
        """删除数据
        """
        self.db.delete(self)
        self.db.commit()

    def refresh(self):
        """刷新本地缓存 (sqlalchemy查询数据库得到结果后,会缓存至本地)
        """
        self.db.refresh(self)
        self.db.commit()

    def update(self, obj_dict):
        """更新数据
        :param obj_dict: 更新数据

        # james = User.query.filter(User.id == 1).first()
        # james.update({'first_name': 'james', 'last_name': 'zhang'})
        # james.save()

        """
        new_dict = {}
        for k, v in obj_dict.items():
            obj_attr = getattr(self.__class__, k, None)
            if obj_attr is None:
                raise core.utils.exceptions.ModelAttrDoesNotExist(
                    "Model %s has not attribute: %s" % (self.__class__.__name__, k))
            new_dict[obj_attr] = v
        self.query.filter(self.__class__.id == self.id).update(new_dict)

    def create(self):
        pass

    def get_or_create(self, default=None, **kwargs):
        """获得 或者 创建一个对象,并返回该对象
        :param default:
        :param kwargs:
        """
        pass

    def to_dict(self):
        """
            将对象的数据属性转换为python字典对象.
        :return:
        """
        column_name_list = [
            value[0] for value in self._sa_instance_state.attrs.items()
        ]
        return dict(
            (column_name, getattr(self, column_name, None)) for column_name in column_name_list
        )

    def __repr__(self):
        return "<" + self.__class__.__name__ + " object>"
